﻿using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using ExpressionTreeDemo.Models;

namespace ExpressionTreeDemo
{
    public class ExpressionTreeExample
    {
        public void Test()
        {
            {
                //Lambda构建表达式树
                Expression<Func<int, int>> expression = x => (x + 1) * (x - 2);

                Func<int, int> func = expression.Compile();
                func.Invoke(1);
            }
            {
                //组装表达式树
                ParameterExpression param = Expression.Parameter(typeof(int), "x");
                BinaryExpression addExpression = Expression.Add(param, Expression.Constant(1));
                BinaryExpression subtractExpression = Expression.Subtract(param, Expression.Constant(2));
                BinaryExpression multiplyExpression = Expression.Multiply(addExpression, subtractExpression);
                Expression<Func<int, int>> expression = Expression.Lambda<Func<int, int>>(multiplyExpression, param);

                Func<int, int> func = expression.Compile();
                func.Invoke(1);
            }
        }

        public void TestComplex()
        {
            {
                Expression<Func<Order, bool>> lambdaExpression = o => o.OrderItems.Select(e => e.Quantity).OrderBy(e => e).FirstOrDefault() > 1;
                Func<Order, bool> lambda = lambdaExpression.Compile();
            }
            {
                ParameterExpression oParameterExpression = Expression.Parameter(typeof(Order), "o");
                MemberExpression orderItemsMemberExpression = Expression.Property(oParameterExpression, "OrderItems");
                MethodInfo selectMethod = typeof(Enumerable).GetMethods().FirstOrDefault(info => info.GetParameters().Length == 2 && info.Name == "Select").MakeGenericMethod(typeof(OrderItem), typeof(int));

                ParameterExpression eParameterExpression = Expression.Parameter(typeof(OrderItem), "e");
                MemberExpression selectQuantityMemberExpression = Expression.Property(eParameterExpression, "Quantity");
                LambdaExpression selectLambdaExpression = Expression.Lambda(selectQuantityMemberExpression, eParameterExpression);

                MethodCallExpression selectCallExpression = Expression.Call(null, selectMethod, orderItemsMemberExpression, selectLambdaExpression);
                MethodInfo orderbyMethod = typeof(Enumerable).GetMethods().FirstOrDefault(info => info.GetParameters().Length == 2 && info.Name == "OrderBy").MakeGenericMethod(typeof(int), typeof(int));

                ParameterExpression xParameterExpression = Expression.Parameter(typeof(int), "x");
                LambdaExpression orderbyLambdaExpression = Expression.Lambda(xParameterExpression, xParameterExpression);

                MethodCallExpression orderbyCallExpression = Expression.Call(null, orderbyMethod, selectCallExpression, orderbyLambdaExpression);
                MethodInfo firstOrDefaultMethod = typeof(Enumerable).GetMethods().FirstOrDefault(info => info.GetParameters().Length == 1 && info.Name == "FirstOrDefault").MakeGenericMethod(typeof(int));
                MethodCallExpression firstOrDefaultCallExpression = Expression.Call(null, firstOrDefaultMethod, orderbyCallExpression);

                BinaryExpression greaterThanExpression = Expression.GreaterThan(firstOrDefaultCallExpression, Expression.Constant(1));
                Expression<Func<Order, bool>> lambdaExpression = Expression.Lambda<Func<Order, bool>>(greaterThanExpression, oParameterExpression);
            }
        }
    }
}
